雖然好的模型和參數可以提高成效,但通常最關鍵還是資料本身。基本上資料的品質決定了八成以上模型的成效,因此大家有必要對自己的資料有所認識和了解。
而在做機器學習的問題,花費最多的通常也是資料處理;我們如果做好資料處理甚至可以大幅提升模型成效。
今天會引導大家認識常用的資料處理的方式,包含常用的 pandas 和 sklearn 套件。我會使用鐵達尼號的資料來示範。
train.pivot_table(columns=['Embarked'])
回顧一下在 pandas篇 有提到可以用 pandas_profiling 可以更方便探索資料
import numpy as np
import pandas as pd
from pandas_profiling import ProfileReport
import seaborn as sns
import matplotlib.pyplot as plt
train = pd.read_csv('train.csv')
profile = ProfileReport(train, title="Pandas Profiling Report")
profile
# Saving the report
profile.to_file("titantic_profiling_report.html")
相關係數 r 介於 -1~1之間
由於 Sex 不是數值,先進行轉換
train['Sex_encoder'] = train.Sex.apply(lambda x: 1 if x=='male' else 0)
train.corr()
我們大概先從相關係數得知以下事情:
發現 Survived 跟 Pclass 、 Sex 有關係,這與我們要預測的方向會很有關係
繪製圖型可以快速掌握資料
sns.countplot(train['Sex'], hue=train['Survived'])
這招非常重要,有時候資料很髒,還是要確定一下是否有無重複的資料或異常。
train[train.duplicated()]
# isnull() 若資料為空值則為 True,反之則為 False
train.isnull().head()
# isnull() + sum() 搭配操作
train.Age.isnull().sum()
# count 僅會計算非空值
train.count()
# 綜合運用 依照空值數量排序
total = train.isnull().sum().sort_values(ascending=False)
print(total)
# 或是調整成百分比型式
percent =(train.isnull().sum()/train.isnull().count()).sort_values(ascending=False)
missing_data = pd.concat([total, percent], axis=1, keys=['Total', 'Percent'])
missing_data
通常有以下幾招
# 使用 dropna
## 預設是只有有空值就刪掉那筆
train.dropna()
# 使用 dropna
## 要全部為空才清楚那一筆資料
train.dropna(how='all')
# 使用 dropna
## 可以指定作用在哪一行
train.dropna(subset=['Fare'])
# 使用 fillna
## 空值都填上 -1
train.fillna(-1)
## 填上平均數
print(train['Age'].mean())
train['Age'].fillna(train['Age'].mean())
## 使用中位數填補
train['Fare'] = train['Fare'].fillna(train['Fare'].median())
## 使用出現次數最多的值填補
most = train['Embarked'].mode()[0]
train['Embarked'] = train['Embarked'].fillna(most)
# 用上下資料進行填補
## 前一個數據進行填充
train.Cabin.fillna(method='pad')
## 後一個數據進行填充
train.Cabin.fillna(method='bfill')
# 用插值法
train.Age.interpolate()
比較一下插值法前後的 displot
sns.distplot(train.Age, kde=True)
sns.distplot(train.Age.interpolate(method ='linear'), kde=True)
我們需要建立一個門檻值去界定離群值,因此我們將資料進行標準化
from sklearn.preprocessing import StandardScaler
#standardizing data
scaled = StandardScaler().fit_transform(train['Fare'][:,np.newaxis]);
low_range = scaled[scaled[:,0].argsort()][:10]
high_range= scaled[scaled[:,0].argsort()][-10:]
print('outer range (low) of the distribution:')
print(low_range)
print('\nouter range (high) of the distribution:')
print(high_range)
from scipy import stats
sns.distplot(x=train["Fare"])
fig = plt.figure()
res = stats.probplot(train['Fare'], plot=plt)
從上圖也很明顯地看到有三個點異常高,可以考慮移除
透過上述的資料處理,相信大家對資料有一點的認識,接著我們可以進行找特徵
有些特徵是差距實在太大,會讓某些模型無法容易學習,因此我們要調整資料的 range,通常是進行標準化。
# 票價的資料有極端值,我們希望可以縮小差距
sns.distplot(x=train["Fare"])
sns.distplot(x=np.log(train["Fare"] + 1))
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler().fit(train[['Age']])
X_scaled = scaler.transform(train[['Age']])
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 1)).fit(train[["Fare"]])
X_scaled = scaler.transform(train[["Fare"]])
資料會被縮放到 [-1, 1]之間
from sklearn.preprocessing import MaxAbsScaler
X = train[["Fare"]]
scaler = MaxAbsScaler().fit(X)
X_scaled = scaler.transform(X)
中位數和四分位數標準化,可以有效縮放離群值
from sklearn.preprocessing import RobustScaler
X = train[["Fare"]]
scaler = RobustScaler().fit(X)
X_scaled = scaler.transform(X)
基本上演算法只是接近這份資料可以達到的預測水平上界,但透過特徵處理,或許可以大幅將預測上界往上移動。因此了解資料後新增特徵,或許預測會更準確!
# 可以新增一個家庭大小,由於有父母數量和兄弟姊妹數量的欄位,合成一個新的欄位
train['family_size'] = train['Parch'] + train['SibSp']